/**不安全网络请求操作类 */
import * as Http from "http";
/**安全网络请求操作类 */
import * as Https from "https";
/**base64处理 */
import MB64 from "./m-b64";
/**类型定义 */
import { MHttpBaseAuth, MHttpSets, MHttpConfig, MHttpResult } from "../m-types";

/**创建基础验证数据 */
export function createBasicAuthToken(auth: MHttpBaseAuth) {
  const { user, pass } = auth;
  const token = user + ":" + pass;
  const hash = MB64.encode(token); // Base64 编码
  return "Basic " + hash;
}

/**网络请求 */
export default class MHttp {
  /**基础配置 */
  public static defaults: MHttpSets = {};
  /**获取当前网络请求器 */
  private static getRequester(config: MHttpConfig) {
    const { base, url } = config;
    const res = url instanceof URL ? new URL(url) : new URL(url || "", base);
    if (base) res.host = base;
    return res.protocol === "https:" ? Https : Http;
  }
  /**获取请求url */
  private static getUrl(config: MHttpConfig): URL {
    const { base, url } = config;
    const parms = config.pararms || {};
    const res = url instanceof URL ? new URL(url) : new URL(url || "", base);
    if (base) res.host = base;
    const search = res.search || "";
    const path = decodeURIComponent(res.pathname || "");
    for (const p in parms) {
      const hasKey = new RegExp(`{${p}}`, "i");
      path.replace(new RegExp(`{${p}}`, "gi"), `${parms[p]}`);
      if (hasKey) delete parms[p];
    }
    res.pathname = path;
    let query = "";
    for (const p in parms) query = `${query}&${p}=${parms[p]}`;
    res.search = search.includes("?")
      ? `${search}${query}`
      : `?${query.replace(/^&+/, "")}`;
    return new URL(res);
  }
  /**合并请求头 */
  private static mergeHeaders(config: MHttpConfig) {
    const { defaults } = MHttp;
    const headers = Object.assign({}, defaults.headers, config.headers);
    if (headers["Authorization"]) return headers;
    const baseAuth = Object.assign({}, defaults.baseAuth, config.baseAuth);
    if (baseAuth.user && baseAuth.pass) {
      headers["Authorization"] = createBasicAuthToken(baseAuth);
    }
    return headers;
  }
  /**合并氢气配置 */
  private static mergeConfig(config: MHttpConfig) {
    const { defaults } = MHttp;
    const res = Object.assign({}, defaults, config);
    res.headers = MHttp.mergeHeaders(config);
    return res;
  }
  /**获取请求配置 */
  private static getOptions(config: MHttpConfig): Http.RequestOptions {
    const { method, headers, timeout } = config;
    const url = MHttp.getUrl(config);
    return {
      method: method || "GET",
      protocol: url.protocol,
      host: url.hostname,
      port: url.port,
      path: `${url.pathname}${url.search}`,
      headers,
      timeout,
    } as Http.RequestOptions;
  }

  /**GET请求 */
  public static async get(
    url: string | URL,
    parm?: any,
    config?: MHttpConfig
  ): Promise<MHttpResult> {
    const pararms = parm || {};
    const c = { ...(config || {}), url, pararms };
    return MHttp.request(c);
  }
  /**POST请求 */
  public static async post(
    url: string | URL,
    data: any,
    config?: MHttpConfig
  ): Promise<MHttpResult> {
    const c = { ...(config || {}), url, data };
    return MHttp.request(c);
  }
  /**PUT请求 */
  public static async put(
    url: string | URL,
    data: any,
    config?: MHttpConfig
  ): Promise<MHttpResult> {
    const c = { ...(config || {}), url, data };
    return MHttp.request(c);
  }
  /**DELETE请求 */
  public static async delete(
    url: string | URL,
    parm?: any,
    config?: MHttpConfig
  ): Promise<MHttpResult> {
    const pararms = parm || {};
    const c = { ...(config || {}), url, pararms };
    return MHttp.request(c);
  }
  /**发起请求 */
  public static async request(config: MHttpConfig): Promise<MHttpResult> {
    return new Promise((resolve, reject) => {
      const conf = MHttp.mergeConfig(config);
      const requester = MHttp.getRequester(conf);
      const options = MHttp.getOptions(conf);
      const req = requester.request(options, (res) => {
        let data = "";
        res.setEncoding("utf8");
        res.on("data", (chunk) => (data += chunk));
        res.on("end", () => resolve(MHttp.getResponse(res, data)));
      });
      req.on("error", (evt: Error) => reject(evt));
      if (config.data) req.write(config.data);
      req.end();
    });
  }
  /**解析返回结果 */
  private static getResponse(res: Http.IncomingMessage, body: string = "") {
    const headers = res.headers || {};
    const status = res.statusCode || 0;
    const serverType =
      headers["content-type"] ||
      headers["Content-type"] ||
      headers["content-Type"] ||
      headers["Content-Type"] ||
      "";
    const type =
      serverType instanceof Array ? serverType.join(";") : serverType;
    body = /application\/json/i.test(type) ? JSON.parse(body) : body;
    const data = body || "";
    return { status, headers, data } as MHttpResult;
  }
}
